﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace Cuemon
{
    /// <summary>
    /// Provide ways to verify conditions a generic way for countless scenarios using true/false propositions.
    /// </summary>
    public static class Condition
    {
        private static readonly Regex RegExEmailAddressValidator = new Regex(@"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|""(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*"")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])",
RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled);

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> has a valid format of an email address.
        /// </summary>
        /// <param name="value">The string to verify has a valid format of an email address.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> has a valid format of an email address; otherwise, <c>false</c>.</returns>
        /// <remarks>
        /// In my search for the most comprehensive and up-to-date regular expression for email address validation, this was the article I choose to implement: http://blog.trojanhunter.com/2012/09/26/the-best-regex-to-validate-an-email-address/.
        /// </remarks>
        public static bool IsEmailAddress(string value)
        {
            if (string.IsNullOrEmpty(value)) { return false; }
            return (RegExEmailAddressValidator.Match(value).Length > 0);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> has a valid format of a <see cref="Guid"/>.
        /// </summary>
        /// <param name="value">The string to verify has a valid format of a <see cref="Guid"/>.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> has a format of a <see cref="Guid"/>; otherwise, <c>false</c>.</returns>
        /// <remarks>
        /// This implementation only evaluates for GUID formats of: <see cref="GuidFormats.DigitFormat"/> | <see cref="GuidFormats.BraceFormat"/> | <see cref="GuidFormats.ParenthesisFormat"/>, eg. 32 digits separated by hyphens; 32 digits separated by hyphens, enclosed in brackets and 32 digits separated by hyphens, enclosed in parentheses.<br/>
        /// The reason not to include <see cref="GuidFormats.NumberFormat"/>, eg. 32 digits is the possible unintended GUID result of a MD5 string representation.
        /// </remarks>
        public static bool IsGuid(string value)
        {
            return IsGuid(value, GuidFormats.BraceFormat | GuidFormats.DigitFormat | GuidFormats.ParenthesisFormat);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> has a valid format of a <see cref="Guid"/>.
        /// </summary>
        /// <param name="value">The string to verify has a valid format of a <see cref="Guid"/>.</param>
        /// <param name="format">A bitmask comprised of one or more <see cref="GuidFormats"/> that specify how the GUID parsing is conducted.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> has a format of a <see cref="Guid"/>; otherwise, <c>false</c>.</returns>
        public static bool IsGuid(string value, GuidFormats format)
        {
            if (string.IsNullOrEmpty(value)) { return false; }
            Guid result;
            return GuidUtility.TryParse(value, format, out result);
        }

        /// <summary>
        /// Determines whether the specified value can be evaluated as a number.
        /// </summary>
        /// <param name="value">The value to be evaluated.</param>
        /// <returns><c>true</c> if the specified value can be evaluated as a number; otherwise, <c>false</c>.</returns>
        /// <remarks>
        /// This method implements a default permitted format of <paramref name="value"/> as <see cref="NumberStyles.Number"/>.<br/>
        /// This method implements a default culture-specific formatting information about <paramref name="value"/> specified to <see cref="CultureInfo.InvariantCulture"/>.
        /// </remarks>
        public static bool IsNumeric(string value)
        {
            return IsNumeric(value, NumberStyles.Number);
        }

        /// <summary>
        /// Determines whether the specified value can be evaluated as a number.
        /// </summary>
        /// <param name="value">The value to be evaluated.</param>
        /// <param name="styles">A bitwise combination of <see cref="NumberStyles"/> values that indicates the permitted format of <paramref name="value"/>.</param>
        /// <returns><c>true</c> if the specified value can be evaluated as a number; otherwise, <c>false</c>.</returns>
        /// <remarks>This method implements a default culture-specific formatting information about <paramref name="value"/> specified to <see cref="CultureInfo.InvariantCulture"/>.</remarks>
        public static bool IsNumeric(string value, NumberStyles styles)
        {
            return IsNumeric(value, styles, CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Determines whether the specified value can be evaluated as a number.
        /// </summary>
        /// <param name="value">The value to be evaluated.</param>
        /// <param name="styles">A bitwise combination of <see cref="NumberStyles"/> values that indicates the permitted format of <paramref name="value"/>.</param>
        /// <param name="provider">An <see cref="IFormatProvider"/> that supplies culture-specific formatting information about <paramref name="value"/>.</param>
        /// <returns><c>true</c> if the specified value can be evaluated as a number; otherwise, <c>false</c>.</returns>
        public static bool IsNumeric(string value, NumberStyles styles, IFormatProvider provider)
        {
            if (string.Equals(value, "NaN", StringComparison.OrdinalIgnoreCase)) { return false; }
            if (string.Equals(value, "Infinity", StringComparison.OrdinalIgnoreCase)) { return false; }
            double outValue;
            return Double.TryParse(value, styles, provider, out outValue);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> has its initial default value.
        /// </summary>
        /// <typeparam name="T">The type of the value.</typeparam>
        /// <param name="value">The object to verify has its initial default value.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> has its initial default value; otherwise, <c>false</c>.</returns>
        public static bool IsDefault<T>(T value)
        {
            return EqualityComparer<T>.Default.Equals(value, default(T));
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> does not have its initial default value.
        /// </summary>
        /// <typeparam name="T">The type of the value.</typeparam>
        /// <param name="value">The object to verify does not have its initial default value.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> does not have its initial default value; otherwise, <c>false</c>.</returns>
        public static bool IsNotDefault<T>(T value)
        {
            return !IsDefault(value);
        }

        /// <summary>
        /// Determines whether the two specified <paramref name="x"/> and <paramref name="y"/> are equal by using the default equality operator from <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> are equal to <paramref name="y"/>; otherwise <c>false</c>.</returns>
        public static bool AreEqual<T>(T x, T y)
        {
            return AreEqual(x, y, EqualityComparer<T>.Default);
        }

        /// <summary>
        /// Determines whether the two specified <paramref name="x"/> and <paramref name="y"/> are equal by using the equality operator.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> implementation to use when comparing <paramref name="x"/> and <paramref name="y"/>.</param>
        /// <returns><c>true</c> if <paramref name="x"/> are equal to <paramref name="y"/>; otherwise <c>false</c>.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="comparer"/> is null.
        /// </exception>
        public static bool AreEqual<T>(T x, T y, IEqualityComparer<T> comparer)
        {
            if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); }
            return comparer.Equals(x, y);
        }

        /// <summary>
        /// Determines whether the two specified <paramref name="x"/> and <paramref name="y"/> are different by using the default equality operator from <typeparamref name="T"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> are different from <paramref name="y"/>; otherwise <c>false</c>.</returns>
        public static bool AreNotEqual<T>(T x, T y)
        {
            return AreNotEqual(x, y, EqualityComparer<T>.Default);
        }

        /// <summary>
        /// Determines whether the two specified <paramref name="x"/> and <paramref name="y"/> are different by using the equality operator.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <param name="comparer">The <see cref="IEqualityComparer{T}"/> implementation to use when comparing <paramref name="x"/> and <paramref name="y"/>.</param>
        /// <returns><c>true</c> if <paramref name="x"/> are different from <paramref name="y"/>; otherwise <c>false</c>.</returns>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="comparer"/> is null.
        /// </exception>
        public static bool AreNotEqual<T>(T x, T y, IEqualityComparer<T> comparer)
        {
            if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); }
            return !AreEqual(x, y, comparer);
        }

        /// <summary>
        /// Determines whether the two specified <paramref name="x"/> object are of the same instance as the <paramref name="y"/> object.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> object are of the same instance as the <paramref name="y"/> object; otherwise <c>false</c>.</returns>
        public static bool AreSame<T>(T x, T y)
        {
            return ReferenceEquals(x, y);
        }

        /// <summary>
        /// Determines whether the two specified <paramref name="x"/> object are not of the same instance as the <paramref name="y"/> object.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> object are not of the same instance as the <paramref name="y"/> object; otherwise <c>false</c>.</returns>
        public static bool AreNotSame<T>(T x, T y)
        {
            return !AreSame(x, y);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> is empty ("").
        /// </summary>
        /// <param name="value">The string to verify is empty.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> is empty; otherwise, <c>false</c>.</returns>
        public static bool IsEmpty(string value)
        {
            if (value == null) { return false; }
            return (value.Length == 0);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> consist only of white-space characters.
        /// </summary>
        /// <param name="value">The string to verify consist only of white-space characters.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> consist only of white-space characters; otherwise, <c>false</c>.</returns>
        public static bool IsWhiteSpace(string value)
        {
            if (value == null) { return false; }
            return value.All(char.IsWhiteSpace);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> is <c>true</c>.
        /// </summary>
        /// <param name="value">The value to verify is <c>true</c>.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> is <c>true</c>; otherwise, <c>false</c>.</returns>
        public static bool IsTrue(bool value)
        {
            return value;
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> is <c>false</c>.
        /// </summary>
        /// <param name="value">The value to verify is <c>false</c>.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> is <c>false</c>; otherwise, <c>false</c>.</returns>
        public static bool IsFalse(bool value)
        {
            return !value;
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> is hexadecimal.
        /// </summary>
        /// <param name="value">The string to verify is hexadecimal.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> is hexadecimal; otherwise, <c>false</c>.</returns>
        public static bool IsHex(string value)
        {
            if (string.IsNullOrEmpty(value)) { return false; }
            if (!NumberUtility.IsEven(value.Length)) { return false; }
            using (StringReader reader = new StringReader(value))
            {
                int even = value.Length / 2;
                for (int i = 0; i < even; ++i)
                {
                    char char1 = (char)reader.Read();
                    char char2 = (char)reader.Read();
                    if (!IsHexDigit(char1) || !IsHexDigit(char2)) { return false; }
                }
            }
            return true;
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> is hexadecimal.
        /// </summary>
        /// <param name="value">The character to verify is hexadecimal.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> is hexadecimal; otherwise, <c>false</c>.</returns>
        public static bool IsHex(char value)
        {
            return IsHexDigit(value);
        }

        private static bool IsHexDigit(char character)
        {
            if (character >= 48 && character <= 57 || character >= 65 && character <= 70)
                return true;
            if (character >= 97)
                return character <= 102;
            return false;
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> is null.
        /// </summary>
        /// <typeparam name="T">The type of the value.</typeparam>
        /// <param name="value">The object to verify is null.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> is null; otherwise, <c>false</c>.</returns>
        public static bool IsNull<T>(T value)
        {
            return (value == null);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="value"/> is not null.
        /// </summary>
        /// <typeparam name="T">The type of the value.</typeparam>
        /// <param name="value">The object to verify is not null.</param>
        /// <returns><c>true</c> if the specified <paramref name="value"/> is not null; otherwise, <c>false</c>.</returns>
        public static bool IsNotNull<T>(T value)
        {
            return !IsNull(value);
        }

        /// <summary>
        /// Determines whether the specified <paramref name="x"/> is greater than <paramref name="y"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> is greater than <paramref name="y"/>; otherwise <c>false</c>.</returns>
        public static bool IsGreaterThan<T>(T x, T y) where T : struct, IConvertible
        {
            return Comparer<T>.Default.Compare(x, y) > 0;
        }

        /// <summary>
        /// Determines whether the specified <paramref name="x"/> is lower than <paramref name="y"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> is lower than <paramref name="y"/>; otherwise <c>false</c>.</returns>
        public static bool IsLowerThan<T>(T x, T y) where T : struct, IConvertible
        {
            return Comparer<T>.Default.Compare(x, y) < 0;
        }

        /// <summary>
        /// Determines whether the specified <paramref name="x"/> is greater than or equal to <paramref name="y"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> is greater than or equal to <paramref name="y"/>; otherwise <c>false</c>.</returns>
        public static bool IsGreaterThanOrEqual<T>(T x, T y) where T : struct, IConvertible
        {
            return (IsGreaterThan(x, y) || AreEqual(x, y));
        }

        /// <summary>
        /// Determines whether the specified <paramref name="x"/> is lower than or equal to <paramref name="y"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns><c>true</c> if <paramref name="x"/> is lower than or equal to <paramref name="y"/>; otherwise <c>false</c>.</returns>
        public static bool IsLowerThanOrEqual<T>(T x, T y) where T : struct, IConvertible
        {
            return (IsLowerThan(x, y) || AreEqual(x, y));
        }

        /// <summary>
        /// Determines whether the specified <paramref name="x"/> is within range of <paramref name="min"/> and <paramref name="max"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The object to compare.</param>
        /// <param name="min">The minimum value of <paramref name="x"/>.</param>
        /// <param name="max">The maximum value of <paramref name="x"/>.</param>
        /// <returns><c>true</c> if <paramref name="x"/> is within range of <paramref name="min"/> and <paramref name="max"/>; otherwise <c>false</c>.</returns>
        public static bool IsWithinRange<T>(T x, T min, T max) where T : struct, IConvertible
        {
            return (IsGreaterThanOrEqual(x, min) && IsLowerThanOrEqual(x, max));
        }

        /// <summary>
        /// Determines whether the specified <paramref name="x"/> is outside the range of <paramref name="min"/> and <paramref name="max"/>.
        /// </summary>
        /// <typeparam name="T">The type of objects to compare.</typeparam>
        /// <param name="x">The object to compare.</param>
        /// <param name="min">The minimum value of <paramref name="x"/>.</param>
        /// <param name="max">The maximum value of <paramref name="x"/>.</param>
        /// <returns><c>true</c> if <paramref name="x"/> is outside the range of <paramref name="min"/> and <paramref name="max"/>; otherwise <c>false</c>.</returns>
        public static bool IsNotWithinRange<T>(T x, T min, T max) where T : struct, IConvertible
        {
            return !IsWithinRange(x, min, max);
        }

        /// <summary>
        /// Returns one of two values depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked and becomes the result; when <c>false</c>, the <paramref name="secondExpression"/> is invoked and becomes the result.</param>
        /// <param name="firstExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <returns>The result of either function delegate <paramref name="firstExpression"/> or function delegate <paramref name="secondExpression"/>.</returns>
        public static TResult TernaryIf<TResult>(bool condition, Func<TResult> firstExpression, Func<TResult> secondExpression)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            return condition ? firstExpression() : secondExpression();
        }

        /// <summary>
        /// Returns one of two values depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T">The type of the parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked and becomes the result; when <c>false</c>, the <paramref name="secondExpression"/> is invoked and becomes the result.</param>
        /// <param name="firstExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg">The parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <returns>The result of either function delegate <paramref name="firstExpression"/> or function delegate <paramref name="secondExpression"/>.</returns>
        public static TResult TernaryIf<T, TResult>(bool condition, Func<T, TResult> firstExpression, Func<T, TResult> secondExpression, T arg)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            return condition ? firstExpression(arg) : secondExpression(arg);
        }

        /// <summary>
        /// Returns one of two values depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked and becomes the result; when <c>false</c>, the <paramref name="secondExpression"/> is invoked and becomes the result.</param>
        /// <param name="firstExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <returns>The result of either function delegate <paramref name="firstExpression"/> or function delegate <paramref name="secondExpression"/>.</returns>
        public static TResult TernaryIf<T1, T2, TResult>(bool condition, Func<T1, T2, TResult> firstExpression, Func<T1, T2, TResult> secondExpression, T1 arg1, T2 arg2)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            return condition ? firstExpression(arg1, arg2) : secondExpression(arg1, arg2);
        }

        /// <summary>
        /// Returns one of two values depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T3">The type of the third parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked and becomes the result; when <c>false</c>, the <paramref name="secondExpression"/> is invoked and becomes the result.</param>
        /// <param name="firstExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg3">The third parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <returns>The result of either function delegate <paramref name="firstExpression"/> or function delegate <paramref name="secondExpression"/>.</returns>
        public static TResult TernaryIf<T1, T2, T3, TResult>(bool condition, Func<T1, T2, T3, TResult> firstExpression, Func<T1, T2, T3, TResult> secondExpression, T1 arg1, T2 arg2, T3 arg3)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            return condition ? firstExpression(arg1, arg2, arg3) : secondExpression(arg1, arg2, arg3);
        }

        /// <summary>
        /// Returns one of two values depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T3">The type of the third parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T4">The type of the fourth parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked and becomes the result; when <c>false</c>, the <paramref name="secondExpression"/> is invoked and becomes the result.</param>
        /// <param name="firstExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg3">The third parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg4">The fourth parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <returns>The result of either function delegate <paramref name="firstExpression"/> or function delegate <paramref name="secondExpression"/>.</returns>
        public static TResult TernaryIf<T1, T2, T3, T4, TResult>(bool condition, Func<T1, T2, T3, T4, TResult> firstExpression, Func<T1, T2, T3, T4, TResult> secondExpression, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            return condition ? firstExpression(arg1, arg2, arg3, arg4) : secondExpression(arg1, arg2, arg3, arg4);
        }

        /// <summary>
        /// Returns one of two values depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T3">The type of the third parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T4">The type of the fourth parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T5">The type of the fifth parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked and becomes the result; when <c>false</c>, the <paramref name="secondExpression"/> is invoked and becomes the result.</param>
        /// <param name="firstExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The function delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg3">The third parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg4">The fourth parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg5">The fifth parameter of the function delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <returns>The result of either function delegate <paramref name="firstExpression"/> or function delegate <paramref name="secondExpression"/>.</returns>
        public static TResult TernaryIf<T1, T2, T3, T4, T5, TResult>(bool condition, Func<T1, T2, T3, T4, T5, TResult> firstExpression, Func<T1, T2, T3, T4, T5, TResult> secondExpression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            return condition ? firstExpression(arg1, arg2, arg3, arg4, arg5) : secondExpression(arg1, arg2, arg3, arg4, arg5);
        }

        /// <summary>
        /// Invokes one of two expressions depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked; when <c>false</c>, the <paramref name="secondExpression"/> is invoked.</param>
        /// <param name="firstExpression">The delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        public static void FlipFlop(bool condition, Action firstExpression, Action secondExpression)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            if (IsTrue(condition)) { firstExpression(); }
            if (IsFalse(condition)) { secondExpression(); }
        }

        /// <summary>
        /// Invokes one of two expressions depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T">The type of the parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked; when <c>false</c>, the <paramref name="secondExpression"/> is invoked.</param>
        /// <param name="firstExpression">The delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg">The parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        public static void FlipFlop<T>(bool condition, Action<T> firstExpression, Action<T> secondExpression, T arg)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            if (IsTrue(condition)) { firstExpression(arg); }
            if (IsFalse(condition)) { secondExpression(arg); }
        }

        /// <summary>
        /// Invokes one of two expressions depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked; when <c>false</c>, the <paramref name="secondExpression"/> is invoked.</param>
        /// <param name="firstExpression">The delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        public static void FlipFlop<T1, T2>(bool condition, Action<T1, T2> firstExpression, Action<T1, T2> secondExpression, T1 arg1, T2 arg2)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            if (IsTrue(condition)) { firstExpression(arg1, arg2); }
            if (IsFalse(condition)) { secondExpression(arg1, arg2); }
        }

        /// <summary>
        /// Invokes one of two expressions depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T3">The type of the third parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked; when <c>false</c>, the <paramref name="secondExpression"/> is invoked.</param>
        /// <param name="firstExpression">The delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg3">The third parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        public static void FlipFlop<T1, T2, T3>(bool condition, Action<T1, T2, T3> firstExpression, Action<T1, T2, T3> secondExpression, T1 arg1, T2 arg2, T3 arg3)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            if (IsTrue(condition)) { firstExpression(arg1, arg2, arg3); }
            if (IsFalse(condition)) { secondExpression(arg1, arg2, arg3); }
        }

        /// <summary>
        /// Invokes one of two expressions depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T3">The type of the third parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T4">The type of the fourth parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked; when <c>false</c>, the <paramref name="secondExpression"/> is invoked.</param>
        /// <param name="firstExpression">The delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg3">The third parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg4">The fourth parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        public static void FlipFlop<T1, T2, T3, T4>(bool condition, Action<T1, T2, T3, T4> firstExpression, Action<T1, T2, T3, T4> secondExpression, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            if (IsTrue(condition)) { firstExpression(arg1, arg2, arg3, arg4); }
            if (IsFalse(condition)) { secondExpression(arg1, arg2, arg3, arg4); }
        }

        /// <summary>
        /// Invokes one of two expressions depending on the value of <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="T1">The type of the first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T2">The type of the second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T3">The type of the third parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T4">The type of the fourth parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <typeparam name="T5">The type of the fifth parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</typeparam>
        /// <param name="condition">When <c>true</c>, the <paramref name="firstExpression"/> is invoked; when <c>false</c>, the <paramref name="secondExpression"/> is invoked.</param>
        /// <param name="firstExpression">The delegate that is invoked when <paramref name="condition"/> is <c>true</c>.</param>
        /// <param name="secondExpression">The delegate that is invoked when <paramref name="condition"/> is <c>false</c>.</param>
        /// <param name="arg1">The first parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg2">The second parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg3">The third parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg4">The fourth parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        /// <param name="arg5">The fifth parameter of the delegates <paramref name="firstExpression"/> and <paramref name="secondExpression"/>.</param>
        public static void FlipFlop<T1, T2, T3, T4, T5>(bool condition, Action<T1, T2, T3, T4, T5> firstExpression, Action<T1, T2, T3, T4, T5> secondExpression, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
        {
            Validator.ThrowIfNull(firstExpression, nameof(firstExpression));
            Validator.ThrowIfNull(secondExpression, nameof(secondExpression));
            if (IsTrue(condition)) { firstExpression(arg1, arg2, arg3, arg4, arg5); }
            if (IsFalse(condition)) { secondExpression(arg1, arg2, arg3, arg4, arg5); }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ConditionBuilder{TTuple}" /> with the specified argument <paramref name="tuple" /> and the result of the starting <paramref name="condition"/>.
        /// </summary>
        /// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>
        /// <typeparam name="TValue">The type of the value for <paramref name="condition"/>.</typeparam>
        /// <param name="value">The argument for <paramref name="condition"/>.</param>
        /// <param name="condition">The delegate that will evaluate <paramref name="value"/>.</param>
        /// <param name="tuple">The argument for the invoker methods of <see cref="ConditionBuilder{TTuple}"/>.</param>
        /// <returns>A new instance of the <see cref="ConditionBuilder{TTuple}" /> initialized with the specified argument <paramref name="tuple" /> and the result of the starting <paramref name="condition"/>.</returns>
        public static ConditionBuilder<TTuple> Initialize<TTuple, TValue>(TTuple tuple, TValue value, Func<TValue, bool> condition) where TTuple : Template
        {
            Validator.ThrowIfNull(condition, nameof(condition));
            return Initialize(tuple, condition(value));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ConditionBuilder{TTuple}" /> with the specified argument <paramref name="tuple" /> and the boolean <paramref name="value"/>.
        /// </summary>
        /// <typeparam name="TTuple">The type of the n-tuple representation of a <see cref="Template"/>.</typeparam>
        /// <param name="tuple">The argument for the invoker methods of <see cref="ConditionBuilder{TTuple}"/>.</param>
        /// <param name="value">The value of a condition that can be either <c>true</c> or <c>false</c>.</param>
        /// <returns>A new instance of the <see cref="ConditionBuilder{TTuple}" /> initialized with the specified argument <paramref name="tuple" /> and the boolean <paramref name="value"/>.</returns>
        public static ConditionBuilder<TTuple> Initialize<TTuple>(TTuple tuple, bool value) where TTuple : Template
        {
            return new ConditionBuilder<TTuple>(tuple).When(value);
        }
    }
}